NOIP2016 普及组

T1:买铅笔

题目描述

P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有 3 种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起见,P老师决定只买同一种包装的铅笔。

商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过 n 支铅笔才够给小朋 友们发礼物。

现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少 n 支铅笔最少需要花费多少钱。

输入输出格式

输入格式

第一行包含一个正整数 n ,表示需要的铅笔数量。

接下来三行,每行用 2 个正整数描述一种包装的铅笔:其中第 1 个整数表示这种 包装内铅笔的数量,第 2 个整数表示这种包装的价格。

保证所有的 7 个数都是不超过 10000 的正整数。

输出格式

1 个整数,表示P老师最少需要花费的钱。

输入输出样例

输入 #1

57
2 2
50 30
30 27

输出 #1

54

输入 #2

9998
128 233
128 2333
128 666

输出 #2

18407

输入 #3

9999
101 1111
1 9999
1111 9999

输入 #3

89991

说明

铅笔的三种包装分别是:

  • 2 支装,价格为 2 ;
  • 50 支装,价格为 30 ;
  • 30 支装,价格为 27

P老师需要购买至少 57 支铅笔。

如果她选择购买第一种包装,那么她需要购买 29 份,共计 2 \times 29 = 58 支,需要花费的钱为 2 \times 29 = 58

实际上,P老师会选择购买第三种包装,这样需要买 2 份。虽然最后买到的铅笔数 量更多了,为 30 \times 2 = 60 支,但花费却减少为 27 \times 2 = 54 ,比第一种少。

对于第二种包装,虽然每支铅笔的价格是最低的,但要够发必须买 2 份,实际的花费达到了 30 \times 2 = 60 ,因此P老师也不会选择。

所以最后输出的答案是 54

【子任务】

子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试 只解决一部分测试数据。

每个测试点的数据规模及特点如下表:

2016 T1.png

上表中“整倍数”的意义为:若为 K ,表示对应数据所需要的铅笔数量 n —定是每种包装铅笔数量的整倍数(这意味着一定可以不用多买铅笔)。

题解:

​​ 本题直接模拟就可以。

​​ 读入每支铅笔的数量与价格,如果购买的数量 n\ mod 数量 =\ 0 的话,ans = min(ans, n / number * money)

​​ 如果购买的数量 n\ mod 数量 !\ 0 的 话,ans = min(ans, (n / number + 1) * money)

​​ 最后,输出 ans 就OK啦!

#include <iostream>
using namespace std;
int main() {
    int n, ans = 0xfffffff;
    cin >> n;
    for (int i = 1; i <= 3; i++) {
        int number, money; //这种铅笔的数量与铅笔的价格
        cin >> number >> money;
        if (n % number == 0)
            ans = min(ans, n / number * money);
        else
            ans = min(ans, (n / number + 1) * money);
    }
    cout << ans << endl;
    return 0;
}

T2:回文日期

题目描述

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 8 位数字表示一个日期,其中,前 4 位代表年份,接下来 2 位代表月份,最后 2 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。

一个 8 位数字是回文的,当且仅当对于所有的TeX parse error: Undefined control sequence \lei从左向右数的第i个数字和第 9-i 个数字(即从右向左数的第 i 个数字)是相同的。

例如:

•对于2016年11月19日,用 8 位数字 20161119 表示,它不是回文的。

•对于2010年1月2日,用 8 位数字 20100102 表示,它是回文的。

•对于2010年10月2日,用 8 位数字 20101002 表示,它不是回文的。

每一年中都有 12 个月份:

其中, 1,3,5,7,8,10,12 月每个月有 31 天; 4,6,9,11 月每个月有 30 天;而对于 2 月,闰年时有 29 天,平年时有 28 天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

1.这个年份是 4 的整数倍,但不是 100 的整数倍;

2.这个年份是 400 的整数倍。

例如:

•以下几个年份都是闰年: 2000,2012,2016

•以下几个年份是平年: 1900,2011,2014

输入输出格式

输入格式

两行,每行包括一个 8 位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证 date\_i 和都是真实存在的日期,且年份部分一定为 4 位数字,且首位数字不为 0

保证 date1 —定不晚于 date2

输出格式

一个整数,表示在 date1 date2 之间,有多少个日期是回文的。

输入输出样例

输入样例#1

20110101
20111231

输出样例#1

1

输入样例#2

20000101
20101231

输出样例#2

2

说明

【样例说明】

对于样例1,符合条件的日期是 20111102

对于样例2,符合条件的日期是 20011002 20100102

【子任务】

对于 60\% 的数据,满足 date1=date2

题解:

​​ 本题是一个字符串问题。

​​ 依次读入起始日期 start 与终止日期 end ,然后,枚举年 year 、月 month 与日 day ,然后, year 赋值为 start\ /\ 10000 , month 赋值为 (start\ \%\ 10000)\ /\ 100 , day 赋值为 start\ /\ 10000

​​ 首先,枚举月与日到次年的 1 月 1 日,然后,将 year 再枚举到 year\ <\ end\ \ /\ 10000 。最后,再次枚举月与日从终止年年的 1 月 1 日枚举到终止年的 (end\ \%\ 10000)\ /\ 100 月与 end\ \%\ 100 日,然后判断是不是回文日期。

​​ 那么,如何判断是不是回文日期呢?无疑,将年月日看作一个八位数字,如果第一位等于最后一位,第二位等于倒数第二位,第三位等于倒数第三位,第四位等于倒数第四位的话那么这个日期便是一个回文日期,也就是 year / 1000 == day % 10 && (year - year / 1000 * 1000) / 100 == day / 10 && (year % 100 - year % 10) / 10 == month % 10 && year % 10 == month /10

​​ 最后,输出回文日期总数就可以啦!

#include <cstring>
#include <iostream>
using namespace std;
int days[13] = {0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void leap(int n) { //判断是不是闰年
    if ((n % 4 == 0 && n % 100 != 0) || n % 400 == 0) //是闰年
        days[2] = 29;
    else //是平年
        days[2] = 28;
}
bool judge (int year, int month, int day) { //判断是不是回文日期
    if (year / 1000 == day % 10 && (year - year / 1000 * 1000) / 100 == day / 10 && (year % 100 - year % 10) / 10 == month % 10 && year % 10 == month /10)
        return true;
    return false;
}
int main() {
    int start, end;
    cin >> start >> end;
    int year = start / 10000, month = (start % 10000) / 100, day = start % 100;
    
    leap(year);
    int ans = 0;
    for (int i = day; i <= days[month]; i++)
        ans += judge(year, month, i);
    month++;
    day = 1;
    for (int i = month; i <= 12; i++)
        for (int j = 1; j <= days[i]; j++)
            ans += judge(year, i, j);
    year++;
    month = day = 1;
    
    while (year < end / 10000) { //枚举年份
        leap(year);
        for (int i = 1; i <= 12; i++)
            for (int j = 1; j <= days[i]; j++)
                ans += judge(year, i, j);
        year++;
    }
    
    leap(year);
    for (int i = 1; i < (end % 10000) / 100; i++)
        for (int j = 1; j <= days[i]; j++)
            ans += judge(year, i, j);
    for (int i = 1; i <= end % 100; i++)
        ans += judge(year, (end % 10000) / 100, i);
    
    cout << ans << endl;
    return 0;
}

T3:海港

题目描述

小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。

小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间ti (单位:秒),船上的乘 客数 k_i ,以及每名乘客的国籍 x_{i,1}, x_{i,2},…,x_{i,k}

小K统计了 n 艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的 24 小时( 24 小时= 86400 秒)内所有乘船到达的乘客来自多少个不同的国家。

形式化地讲,你需要计算 n 条信息。对于输出的第 i 条信息,你需要统计满足 t_i-86400<t_p \le t_i 的船只 p ,在所有的 x_{p,j} 中,总共有多少个不同的数。

输入输出格式

输入格式 第一行输入一个正整数 n ,表示小K统计了 n 艘船的信息。

接下来 n 行,每行描述一艘船的信息:前两个整数 t_i k_i 分别表示这艘船到达海港的时间和船上的乘客数量,接下来 k_i 个整数 x_{i,j} 表示船上乘客的国籍。

保证输入的 t_i 是递增的,单位是秒;表示从小K第一次上班开始计时,这艘船在第 t_i 秒到达海港。

保证 1 \le n \le 10^5 \sum{ki} \le 3*10^5 1\le x(i,j) \le 10^5 1 \le t(i-1)\le ti \le 10^9

其中 \sum{ki} 表示所有的 k_i 的和。

输出格式 输出 n 行,第 i 行输出一个整数表示第 i 艘船到达后的统计信息。

输入输出样例

输入样例#1

3
144122
2223
1013

输出样例#1

3
4
4

输入样例#2

4
1 4 1 2 2 3
3 2 2 3
86401 2 3 4
86402 1 5

输出样例#2

3
3
3
4

说明

【样例解释1】

第一艘船在第 1 秒到达海港,最近 24 小时到达的船是第一艘船,共有 4 个乘客,分别是来自国家 4,1,2,2 ,共来自 3 个不同的国家;

第二艘船在第 2 秒到达海港,最近 24 小时到达的船是第一艘船和第二艘船,共有 4+2=6 个乘客,分别是来自国家 4,1,2,2,2,3 ,共来自 4 个不同的国家;

第三艘船在第 10 秒到达海港,最近 24 小时到达的船是第一艘船、第二艘船和第三艘船,共有 4+2+1=7 个乘客,分别是来自国家 4,1,2,2,2,3,3 ,共来自4 个不同的国家。

【样例解释2】

第一艘船在第 1 秒到达海港,最近 24 小时到达的船是第一艘船,共有 4 个乘客,分别是来自国家 1,2,2,3 ,共来自 3 个不同的国家。

第二艘船在第 3 秒到达海港,最近 24 小时到达的船是第一艘船和第二艘船,共有 4+2=6 个乘客,分别是来自国家 1,2,2,3,2,3 ,共来自 3 个不同的国家。

第三艘船在第 86401 秒到达海港,最近 24 小时到达的船是第二艘船和第三艘船,共有 2+2=4 个乘客,分别是来自国家 2,3,3,4 ,共来自 3 个不同的国家。

第四艘船在第 86402 秒到达海港,最近 24 小时到达的船是第二艘船、第三艘船和第四艘船,共有 2+2+1=5 个乘客,分别是来自国家 2,3,3,4,5 ,共来自 4 个不同的国家。

【数据范围】

2016 T3.png

题解:

​​ 由于 \sum{ki} \le 3*10^5 ,所以我们可以按人头来存储。

​​ 我们读入每艘船到达海港的时间和船上的乘客数量。然后依次每句每一个乘客的国籍 passenger[p][1] ,并将 passenger[p][2] 赋值为时间 t 。用 bucket 数组存储每个乘客的国籍,然后将此乘客所属的国家 bucket 加一。若 bucket[passenger[p][1] 1 的话那么 ans 便加 1

​​ 然后我们枚举时间,如果 t 减去 passenger[i][2]\ >=\ 86400 的话,那么便代表这个乘客的时间超过一天了,因此便将这个乘客的信息全部删除。再特判一下,如果 bucket[passenger[i][1]]\ ==\ 0 的话,那么 ans 便减 1 。最后,输出 ans 便可以了!

#include <iostream>
using namespace std;
int passenger[300001][3], bucket[100001];
int main() {
    int n, p = 1, start = 1, ans = 0;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int t, k;
        cin >> t >> k;
        for (int j = 1; j <= k; j++) {
            cin >> passenger[p][1];
            passenger[p][2] = t;
            bucket[passenger[p][1]]++;
            if (bucket[passenger[p][1]] == 1) //第一个被加进来的
                ans++;
            p++;
        }
        for (int i = start; i <= t; i++) {
            if (t - passenger[i][2] >= 86400) { //超出这个时间了
                if (bucket[passenger[i][1]] > 0) {
                    bucket[passenger[i][1]]--;
                    if (bucket[passenger[i][1]] == 0)
                        ans--;
                    start++;
                }
            } else {
                break;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

T4:魔法阵

题目描述

六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。

大魔法师有 m 个魔法物品,编号分别为 1,2,...,m 。每个物品具有一个魔法值,我们用 X_i 表示编号为i的物品的魔法值。每个魔法值Xi是不超过n的正整数,可能有多个物品的魔法值相同。

大魔法师认为,当且仅当四个编号为 a,b,c,d 的魔法物品满足 x_a<x_b<x_c<x_d,X_b-X_a=2(X_d-X_c) ,并且 x_b-x_a<(x_c-x_b)/3 时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的 A 物品, B 物品, C 物品, D 物品。

现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的 A 物品出现的次数,作为 B 物品的次数,作为 C 物品的次数,和作为 D 物品的次数。

输入格式

第一行包含两个空格隔开的正整数 n,m

接下来 m 行,每行一个正整数,第 i+1 行的正整数表示 X_i ,即编号为 i 的物品的魔法值。

保证 1 \le n \le 15000 , 1 \le m \le 40000 , 1 \le Xi \le n 。每个 X_i 是分别在合法范围内等概率随机生成的。

输出格式

m 行,每行 4 个整数。第 i 行的 4 个整数依次表示编号为 i 的物品作 为 A,B,C,D 物品分别出现的次数。

保证标准输出中的每个数都不会超过 10^9 。每行相邻的两个数之间用恰好一个空格隔开。

输入输出样例

输入样例#1

30 8
1
24
7
28
5
29
26
24

输出样例#1

4 0 0 0
0 0 1 0
0 2 0 0
0 0 1 1
1 3 0 0
0 0 0 2
0 0 2 2
0 0 1 0

输入样例#2

15 15
1 
2 
3 
4 
5
6 
7 
8 
9
10
11
12
13
14
15

输出样例#2

5 0 0 0
4 0 0 0
3 5 0 0
2 4 0 0
1 3 0 0
0 2 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 0 1 0
0 0 2 1
0 0 3 2
0 0 4 3
0 0 5 4
0 0 0 5

说明/提示

【样例解释1】

共有 5 个魔法阵,分别为:

物品 1,3,7,6 ,其魔法值分别为 1,7,26,29 ;

物品 1,5,2,7 ,其魔法值分别为 1,5,24,26 ;

物品 1,5,7,4 ,其魔法值分别为 1,5,26,28 ;

物品 1,5,8,7 ,其魔法值分别为 1,5,24,26 ;

物品 5,3,4,6 ,其魔法值分别为 5,7,28,29

以物品 5 为例,它作为 A 物品出现了 1 次,作为 B 物品出现了 3 次,没有作为 C 物品或者 D 物品出现,所以这一行输出的四个数依次为 1,3,0,0

此外,如果我们将输出看作一个 m 4 列的矩阵,那么每一列上的 m 个数之和都应等于魔法阵的总数。所以,如果你的输出不满足这个性质,那么这个输出一定不正确。你可以通过这个性质在一定程度上检查你的输出的正确性。

【数据规模】

2016 T4-2.png

占坑待填。。。